{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# How to manage memory\n",
    "\n",
    ":::info Prerequisites\n",
    "\n",
    "This guide assumes familiarity with the following:\n",
    "\n",
    "- [Chatbots](/docs/tutorials/chatbot)\n",
    "\n",
    ":::\n",
    "\n",
    "A key feature of chatbots is their ability to use content of previous conversation turns as context. This state management can take several forms, including:\n",
    "\n",
    "- Simply stuffing previous messages into a chat model prompt.\n",
    "- The above, but trimming old messages to reduce the amount of distracting information the model has to deal with.\n",
    "- More complex modifications like synthesizing summaries for long running conversations.\n",
    "\n",
    "We’ll go into more detail on a few techniques below!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "You’ll need to install a few packages, and set any LLM API keys:\n",
    "\n",
    "Let’s also set up a chat model that we’ll use for the below examples:\n",
    "\n",
    "```{=mdx}\n",
    "import ChatModelTabs from \"@theme/ChatModelTabs\";\n",
    "\n",
    "<ChatModelTabs />\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Message passing\n",
    "\n",
    "The simplest form of memory is simply passing chat history messages into a chain. Here’s an example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage {\n",
       "  lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "  lc_kwargs: {\n",
       "    content: \u001b[32m`I said \"J'adore la programmation,\" which means \"I love programming\" in French.`\u001b[39m,\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: [],\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "  content: \u001b[32m`I said \"J'adore la programmation,\" which means \"I love programming\" in French.`\u001b[39m,\n",
       "  name: \u001b[90mundefined\u001b[39m,\n",
       "  additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "  response_metadata: {\n",
       "    tokenUsage: { completionTokens: \u001b[33m21\u001b[39m, promptTokens: \u001b[33m61\u001b[39m, totalTokens: \u001b[33m82\u001b[39m },\n",
       "    finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "  },\n",
       "  tool_calls: [],\n",
       "  invalid_tool_calls: []\n",
       "}"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import { HumanMessage, AIMessage } from \"@langchain/core/messages\";\n",
    "import {\n",
    "  ChatPromptTemplate,\n",
    "  MessagesPlaceholder,\n",
    "} from \"@langchain/core/prompts\";\n",
    "\n",
    "const prompt = ChatPromptTemplate.fromMessages([\n",
    "  [\n",
    "    \"system\",\n",
    "    \"You are a helpful assistant. Answer all questions to the best of your ability.\",\n",
    "  ],\n",
    "  new MessagesPlaceholder(\"messages\"),\n",
    "]);\n",
    "\n",
    "const chain = prompt.pipe(llm);\n",
    "\n",
    "await chain.invoke({\n",
    "  messages: [\n",
    "    new HumanMessage(\n",
    "      \"Translate this sentence from English to French: I love programming.\"\n",
    "    ),\n",
    "    new AIMessage(\"J'adore la programmation.\"),\n",
    "    new HumanMessage(\"What did you just say?\"),\n",
    "  ],\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that by passing the previous conversation into a chain, it can use it as context to answer questions. This is the basic concept underpinning chatbot memory - the rest of the guide will demonstrate convenient techniques for passing or reformatting messages."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Chat history\n",
    "\n",
    "It’s perfectly fine to store and pass messages directly as an array, but we can use LangChain’s built-in message history class to store and load messages as well. Instances of this class are responsible for storing and loading chat messages from persistent storage. LangChain integrates with many providers but for this demo we will use an ephemeral demo class.\n",
    "\n",
    "Here’s an example of the API:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[\n",
       "  HumanMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"Translate this sentence from English to French: I love programming.\"\u001b[39m,\n",
       "      additional_kwargs: {},\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"Translate this sentence from English to French: I love programming.\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: {},\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  AIMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"J'adore la programmation.\"\u001b[39m,\n",
       "      tool_calls: [],\n",
       "      invalid_tool_calls: [],\n",
       "      additional_kwargs: {},\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"J'adore la programmation.\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: {},\n",
       "    response_metadata: {},\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: []\n",
       "  }\n",
       "]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import { ChatMessageHistory } from \"langchain/stores/message/in_memory\";\n",
    "\n",
    "const demoEphemeralChatMessageHistory = new ChatMessageHistory();\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(\n",
    "  new HumanMessage(\n",
    "    \"Translate this sentence from English to French: I love programming.\"\n",
    "  )\n",
    ");\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(\n",
    "  new AIMessage(\"J'adore la programmation.\")\n",
    ");\n",
    "\n",
    "await demoEphemeralChatMessageHistory.getMessages();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can use it directly to store conversation turns for our chain:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage {\n",
       "  lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "  lc_kwargs: {\n",
       "    content: \u001b[32m'You just asked me to translate the sentence \"I love programming\" from English to French.'\u001b[39m,\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: [],\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "  content: \u001b[32m'You just asked me to translate the sentence \"I love programming\" from English to French.'\u001b[39m,\n",
       "  name: \u001b[90mundefined\u001b[39m,\n",
       "  additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "  response_metadata: {\n",
       "    tokenUsage: { completionTokens: \u001b[33m18\u001b[39m, promptTokens: \u001b[33m73\u001b[39m, totalTokens: \u001b[33m91\u001b[39m },\n",
       "    finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "  },\n",
       "  tool_calls: [],\n",
       "  invalid_tool_calls: []\n",
       "}"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await demoEphemeralChatMessageHistory.clear();\n",
    "\n",
    "const input1 =\n",
    "  \"Translate this sentence from English to French: I love programming.\";\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(new HumanMessage(input1));\n",
    "\n",
    "const response = await chain.invoke({\n",
    "  messages: await demoEphemeralChatMessageHistory.getMessages(),\n",
    "});\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(response);\n",
    "\n",
    "const input2 = \"What did I just ask you?\";\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(new HumanMessage(input2));\n",
    "\n",
    "await chain.invoke({\n",
    "  messages: await demoEphemeralChatMessageHistory.getMessages(),\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Automatic history management\n",
    "\n",
    "The previous examples pass messages to the chain explicitly. This is a completely acceptable approach, but it does require external management of new messages. LangChain also includes an wrapper for LCEL chains that can handle this process automatically called `RunnableWithMessageHistory`.\n",
    "\n",
    "To show how it works, let’s slightly modify the above prompt to take a final `input` variable that populates a `HumanMessage` template after the chat history. This means that we will expect a `chat_history` parameter that contains all messages BEFORE the current messages instead of all messages:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "const runnableWithMessageHistoryPrompt = ChatPromptTemplate.fromMessages([\n",
    "  [\n",
    "    \"system\",\n",
    "    \"You are a helpful assistant. Answer all questions to the best of your ability.\",\n",
    "  ],\n",
    "  new MessagesPlaceholder(\"chat_history\"),\n",
    "  [\"human\", \"{input}\"],\n",
    "]);\n",
    "\n",
    "const chain2 = runnableWithMessageHistoryPrompt.pipe(llm);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We’ll pass the latest input to the conversation here and let the `RunnableWithMessageHistory` class wrap our chain and do the work of appending that `input` variable to the chat history.\n",
    "\n",
    "Next, let’s declare our wrapped chain:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "import { RunnableWithMessageHistory } from \"@langchain/core/runnables\";\n",
    "\n",
    "const demoEphemeralChatMessageHistoryForChain = new ChatMessageHistory();\n",
    "\n",
    "const chainWithMessageHistory = new RunnableWithMessageHistory({\n",
    "  runnable: chain2,\n",
    "  getMessageHistory: (_sessionId) => demoEphemeralChatMessageHistoryForChain,\n",
    "  inputMessagesKey: \"input\",\n",
    "  historyMessagesKey: \"chat_history\",\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This class takes a few parameters in addition to the chain that we want to wrap:\n",
    "\n",
    "- A factory function that returns a message history for a given session id. This allows your chain to handle multiple users at once by loading different messages for different conversations.\n",
    "- An `inputMessagesKey` that specifies which part of the input should be tracked and stored in the chat history. In this example, we want to track the string passed in as input.\n",
    "- A `historyMessagesKey` that specifies what the previous messages should be injected into the prompt as. Our prompt has a `MessagesPlaceholder` named `chat_history`, so we specify this property to match.\n",
    "  (For chains with multiple outputs) an `outputMessagesKey` which specifies which output to store as history. This is the inverse of `inputMessagesKey`.\n",
    "\n",
    "We can invoke this new chain as normal, with an additional `configurable` field that specifies the particular `sessionId` to pass to the factory function. This is unused for the demo, but in real-world chains, you’ll want to return a chat history corresponding to the passed session:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage {\n",
       "  lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "  lc_kwargs: {\n",
       "    content: \u001b[32m`The translation of \"I love programming\" in French is \"J'adore la programmation.\"`\u001b[39m,\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: [],\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "  content: \u001b[32m`The translation of \"I love programming\" in French is \"J'adore la programmation.\"`\u001b[39m,\n",
       "  name: \u001b[90mundefined\u001b[39m,\n",
       "  additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "  response_metadata: {\n",
       "    tokenUsage: { completionTokens: \u001b[33m20\u001b[39m, promptTokens: \u001b[33m39\u001b[39m, totalTokens: \u001b[33m59\u001b[39m },\n",
       "    finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "  },\n",
       "  tool_calls: [],\n",
       "  invalid_tool_calls: []\n",
       "}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await chainWithMessageHistory.invoke(\n",
    "  {\n",
    "    input:\n",
    "      \"Translate this sentence from English to French: I love programming.\",\n",
    "  },\n",
    "  { configurable: { sessionId: \"unused\" } }\n",
    ");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage {\n",
       "  lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "  lc_kwargs: {\n",
       "    content: \u001b[32m'You just asked for the translation of the sentence \"I love programming\" from English to French.'\u001b[39m,\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: [],\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "  content: \u001b[32m'You just asked for the translation of the sentence \"I love programming\" from English to French.'\u001b[39m,\n",
       "  name: \u001b[90mundefined\u001b[39m,\n",
       "  additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "  response_metadata: {\n",
       "    tokenUsage: { completionTokens: \u001b[33m19\u001b[39m, promptTokens: \u001b[33m74\u001b[39m, totalTokens: \u001b[33m93\u001b[39m },\n",
       "    finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "  },\n",
       "  tool_calls: [],\n",
       "  invalid_tool_calls: []\n",
       "}"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await chainWithMessageHistory.invoke(\n",
    "  {\n",
    "    input: \"What did I just ask you?\",\n",
    "  },\n",
    "  { configurable: { sessionId: \"unused\" } }\n",
    ");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Modifying chat history\n",
    "\n",
    "Modifying stored chat messages can help your chatbot handle a variety of situations. Here are some examples:\n",
    "\n",
    "### Trimming messages\n",
    "\n",
    "LLMs and chat models have limited context windows, and even if you’re not directly hitting limits, you may want to limit the amount of distraction the model has to deal with. One solution is to only load and store the most recent `n` messages. Let’s use an example history with some preloaded messages:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[\n",
       "  HumanMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"Hey there! I'm Nemo.\"\u001b[39m,\n",
       "      additional_kwargs: {},\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"Hey there! I'm Nemo.\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: {},\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  AIMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"Hello!\"\u001b[39m,\n",
       "      tool_calls: [],\n",
       "      invalid_tool_calls: [],\n",
       "      additional_kwargs: {},\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"Hello!\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: {},\n",
       "    response_metadata: {},\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: []\n",
       "  },\n",
       "  HumanMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"How are you today?\"\u001b[39m,\n",
       "      additional_kwargs: {},\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"How are you today?\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: {},\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  AIMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"Fine thanks!\"\u001b[39m,\n",
       "      tool_calls: [],\n",
       "      invalid_tool_calls: [],\n",
       "      additional_kwargs: {},\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"Fine thanks!\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: {},\n",
       "    response_metadata: {},\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: []\n",
       "  }\n",
       "]"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await demoEphemeralChatMessageHistory.clear();\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(\n",
    "  new HumanMessage(\"Hey there! I'm Nemo.\")\n",
    ");\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(new AIMessage(\"Hello!\"));\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(\n",
    "  new HumanMessage(\"How are you today?\")\n",
    ");\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(new AIMessage(\"Fine thanks!\"));\n",
    "\n",
    "await demoEphemeralChatMessageHistory.getMessages();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let’s use this message history with the `RunnableWithMessageHistory` chain we declared above:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage {\n",
       "  lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "  lc_kwargs: {\n",
       "    content: \u001b[32m\"Your name is Nemo!\"\u001b[39m,\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: [],\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "  content: \u001b[32m\"Your name is Nemo!\"\u001b[39m,\n",
       "  name: \u001b[90mundefined\u001b[39m,\n",
       "  additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "  response_metadata: {\n",
       "    tokenUsage: { completionTokens: \u001b[33m6\u001b[39m, promptTokens: \u001b[33m66\u001b[39m, totalTokens: \u001b[33m72\u001b[39m },\n",
       "    finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "  },\n",
       "  tool_calls: [],\n",
       "  invalid_tool_calls: []\n",
       "}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "const chainWithMessageHistory2 = new RunnableWithMessageHistory({\n",
    "  runnable: chain2,\n",
    "  getMessageHistory: (_sessionId) => demoEphemeralChatMessageHistory,\n",
    "  inputMessagesKey: \"input\",\n",
    "  historyMessagesKey: \"chat_history\",\n",
    "});\n",
    "\n",
    "await chainWithMessageHistory2.invoke(\n",
    "  {\n",
    "    input: \"What's my name?\",\n",
    "  },\n",
    "  { configurable: { sessionId: \"unused\" } }\n",
    ");"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see the chain remembers the preloaded name.\n",
    "\n",
    "But let’s say we have a very small context window, and we want to trim the number of messages passed to the chain to only the 2 most recent ones. We can use the `clear` method to remove messages and re-add them to the history. We don’t have to, but let’s put this method at the front of our chain to ensure it’s always called:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "import {\n",
    "  RunnablePassthrough,\n",
    "  RunnableSequence,\n",
    "} from \"@langchain/core/runnables\";\n",
    "\n",
    "const trimMessages = async (_chainInput: Record<string, any>) => {\n",
    "  const storedMessages = await demoEphemeralChatMessageHistory.getMessages();\n",
    "  if (storedMessages.length <= 2) {\n",
    "    return false;\n",
    "  }\n",
    "  await demoEphemeralChatMessageHistory.clear();\n",
    "  for (const message of storedMessages.slice(-2)) {\n",
    "    demoEphemeralChatMessageHistory.addMessage(message);\n",
    "  }\n",
    "  return true;\n",
    "};\n",
    "\n",
    "const chainWithTrimming = RunnableSequence.from([\n",
    "  RunnablePassthrough.assign({ messages_trimmed: trimMessages }),\n",
    "  chainWithMessageHistory2,\n",
    "]);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let’s call this new chain and check the messages afterwards:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage {\n",
       "  lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "  lc_kwargs: {\n",
       "    content: \u001b[32m'P. Sherman is a fictional character who lives at 42 Wallaby Way, Sydney, from the movie \"Finding Nem'\u001b[39m... 3 more characters,\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: [],\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "  content: \u001b[32m'P. Sherman is a fictional character who lives at 42 Wallaby Way, Sydney, from the movie \"Finding Nem'\u001b[39m... 3 more characters,\n",
       "  name: \u001b[90mundefined\u001b[39m,\n",
       "  additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "  response_metadata: {\n",
       "    tokenUsage: { completionTokens: \u001b[33m26\u001b[39m, promptTokens: \u001b[33m53\u001b[39m, totalTokens: \u001b[33m79\u001b[39m },\n",
       "    finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "  },\n",
       "  tool_calls: [],\n",
       "  invalid_tool_calls: []\n",
       "}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await chainWithTrimming.invoke(\n",
    "  {\n",
    "    input: \"Where does P. Sherman live?\",\n",
    "  },\n",
    "  { configurable: { sessionId: \"unused\" } }\n",
    ");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[\n",
       "  HumanMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"What's my name?\"\u001b[39m,\n",
       "      additional_kwargs: {},\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"What's my name?\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: {},\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  AIMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"Your name is Nemo!\"\u001b[39m,\n",
       "      tool_calls: [],\n",
       "      invalid_tool_calls: [],\n",
       "      additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"Your name is Nemo!\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {\n",
       "      tokenUsage: { completionTokens: \u001b[33m6\u001b[39m, promptTokens: \u001b[33m66\u001b[39m, totalTokens: \u001b[33m72\u001b[39m },\n",
       "      finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "    },\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: []\n",
       "  },\n",
       "  HumanMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"Where does P. Sherman live?\"\u001b[39m,\n",
       "      additional_kwargs: {},\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"Where does P. Sherman live?\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: {},\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  AIMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m'P. Sherman is a fictional character who lives at 42 Wallaby Way, Sydney, from the movie \"Finding Nem'\u001b[39m... 3 more characters,\n",
       "      tool_calls: [],\n",
       "      invalid_tool_calls: [],\n",
       "      additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m'P. Sherman is a fictional character who lives at 42 Wallaby Way, Sydney, from the movie \"Finding Nem'\u001b[39m... 3 more characters,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {\n",
       "      tokenUsage: { completionTokens: \u001b[33m26\u001b[39m, promptTokens: \u001b[33m53\u001b[39m, totalTokens: \u001b[33m79\u001b[39m },\n",
       "      finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "    },\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: []\n",
       "  }\n",
       "]"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await demoEphemeralChatMessageHistory.getMessages();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And we can see that our history has removed the two oldest messages while still adding the most recent conversation at the end. The next time the chain is called, `trimMessages` will be called again, and only the two most recent messages will be passed to the model. In this case, this means that the model will forget the name we gave it the next time we invoke it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage {\n",
       "  lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "  lc_kwargs: {\n",
       "    content: \u001b[32m\"I'm sorry, I don't have access to your personal information. Can I help you with anything else?\"\u001b[39m,\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: [],\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "  content: \u001b[32m\"I'm sorry, I don't have access to your personal information. Can I help you with anything else?\"\u001b[39m,\n",
       "  name: \u001b[90mundefined\u001b[39m,\n",
       "  additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "  response_metadata: {\n",
       "    tokenUsage: { completionTokens: \u001b[33m22\u001b[39m, promptTokens: \u001b[33m73\u001b[39m, totalTokens: \u001b[33m95\u001b[39m },\n",
       "    finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "  },\n",
       "  tool_calls: [],\n",
       "  invalid_tool_calls: []\n",
       "}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await chainWithTrimming.invoke(\n",
    "  {\n",
    "    input: \"What is my name?\",\n",
    "  },\n",
    "  { configurable: { sessionId: \"unused\" } }\n",
    ");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[\n",
       "  HumanMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"Where does P. Sherman live?\"\u001b[39m,\n",
       "      additional_kwargs: {},\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"Where does P. Sherman live?\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: {},\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  AIMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m'P. Sherman is a fictional character who lives at 42 Wallaby Way, Sydney, from the movie \"Finding Nem'\u001b[39m... 3 more characters,\n",
       "      tool_calls: [],\n",
       "      invalid_tool_calls: [],\n",
       "      additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m'P. Sherman is a fictional character who lives at 42 Wallaby Way, Sydney, from the movie \"Finding Nem'\u001b[39m... 3 more characters,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {\n",
       "      tokenUsage: { completionTokens: \u001b[33m26\u001b[39m, promptTokens: \u001b[33m53\u001b[39m, totalTokens: \u001b[33m79\u001b[39m },\n",
       "      finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "    },\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: []\n",
       "  },\n",
       "  HumanMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"What is my name?\"\u001b[39m,\n",
       "      additional_kwargs: {},\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"What is my name?\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: {},\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  AIMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"I'm sorry, I don't have access to your personal information. Can I help you with anything else?\"\u001b[39m,\n",
       "      tool_calls: [],\n",
       "      invalid_tool_calls: [],\n",
       "      additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"I'm sorry, I don't have access to your personal information. Can I help you with anything else?\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {\n",
       "      tokenUsage: { completionTokens: \u001b[33m22\u001b[39m, promptTokens: \u001b[33m73\u001b[39m, totalTokens: \u001b[33m95\u001b[39m },\n",
       "      finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "    },\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: []\n",
       "  }\n",
       "]"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await demoEphemeralChatMessageHistory.getMessages();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Summary memory\n",
    "\n",
    "We can use this same pattern in other ways too. For example, we could use an additional LLM call to generate a summary of the conversation before calling our chain. Let’s recreate our chat history and chatbot chain:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "await demoEphemeralChatMessageHistory.clear();\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(\n",
    "  new HumanMessage(\"Hey there! I'm Nemo.\")\n",
    ");\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(new AIMessage(\"Hello!\"));\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(\n",
    "  new HumanMessage(\"How are you today?\")\n",
    ");\n",
    "\n",
    "await demoEphemeralChatMessageHistory.addMessage(new AIMessage(\"Fine thanks!\"));"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "const runnableWithSummaryMemoryPrompt = ChatPromptTemplate.fromMessages([\n",
    "  [\n",
    "    \"system\",\n",
    "    \"You are a helpful assistant. Answer all questions to the best of your ability. The provided chat history includes facts about the user you are speaking with.\",\n",
    "  ],\n",
    "  new MessagesPlaceholder(\"chat_history\"),\n",
    "  [\"human\", \"{input}\"],\n",
    "]);\n",
    "\n",
    "const summaryMemoryChain = runnableWithSummaryMemoryPrompt.pipe(llm);\n",
    "\n",
    "const chainWithMessageHistory3 = new RunnableWithMessageHistory({\n",
    "  runnable: summaryMemoryChain,\n",
    "  getMessageHistory: (_sessionId) => demoEphemeralChatMessageHistory,\n",
    "  inputMessagesKey: \"input\",\n",
    "  historyMessagesKey: \"chat_history\",\n",
    "});"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And now, let’s create a function that will distill previous interactions into a summary. We can add this one to the front of the chain too:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "const summarizeMessages = async (_chainInput: Record<string, any>) => {\n",
    "  const storedMessages = await demoEphemeralChatMessageHistory.getMessages();\n",
    "  if (storedMessages.length === 0) {\n",
    "    return false;\n",
    "  }\n",
    "  const summarizationPrompt = ChatPromptTemplate.fromMessages([\n",
    "    new MessagesPlaceholder(\"chat_history\"),\n",
    "    [\n",
    "      \"user\",\n",
    "      \"Distill the above chat messages into a single summary message. Include as many specific details as you can.\",\n",
    "    ],\n",
    "  ]);\n",
    "  const summarizationChain = summarizationPrompt.pipe(llm);\n",
    "  const summaryMessage = await summarizationChain.invoke({\n",
    "    chat_history: storedMessages,\n",
    "  });\n",
    "  await demoEphemeralChatMessageHistory.clear();\n",
    "  demoEphemeralChatMessageHistory.addMessage(summaryMessage);\n",
    "  return true;\n",
    "};\n",
    "\n",
    "const chainWithSummarization = RunnableSequence.from([\n",
    "  RunnablePassthrough.assign({\n",
    "    messages_summarized: summarizeMessages,\n",
    "  }),\n",
    "  chainWithMessageHistory3,\n",
    "]);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let’s see if it remembers the name we gave it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage {\n",
       "  lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "  lc_kwargs: {\n",
       "    content: \u001b[32m'You introduced yourself as \"Nemo.\"'\u001b[39m,\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: [],\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "  content: \u001b[32m'You introduced yourself as \"Nemo.\"'\u001b[39m,\n",
       "  name: \u001b[90mundefined\u001b[39m,\n",
       "  additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "  response_metadata: {\n",
       "    tokenUsage: { completionTokens: \u001b[33m8\u001b[39m, promptTokens: \u001b[33m87\u001b[39m, totalTokens: \u001b[33m95\u001b[39m },\n",
       "    finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "  },\n",
       "  tool_calls: [],\n",
       "  invalid_tool_calls: []\n",
       "}"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await chainWithSummarization.invoke(\n",
    "  {\n",
    "    input: \"What did I say my name was?\",\n",
    "  },\n",
    "  {\n",
    "    configurable: { sessionId: \"unused\" },\n",
    "  }\n",
    ");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[\n",
       "  AIMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"The conversation consists of a greeting from someone named Nemo and a general inquiry about their we\"\u001b[39m... 86 more characters,\n",
       "      tool_calls: [],\n",
       "      invalid_tool_calls: [],\n",
       "      additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"The conversation consists of a greeting from someone named Nemo and a general inquiry about their we\"\u001b[39m... 86 more characters,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {\n",
       "      tokenUsage: { completionTokens: \u001b[33m34\u001b[39m, promptTokens: \u001b[33m62\u001b[39m, totalTokens: \u001b[33m96\u001b[39m },\n",
       "      finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "    },\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: []\n",
       "  },\n",
       "  HumanMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m\"What did I say my name was?\"\u001b[39m,\n",
       "      additional_kwargs: {},\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m\"What did I say my name was?\"\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: {},\n",
       "    response_metadata: {}\n",
       "  },\n",
       "  AIMessage {\n",
       "    lc_serializable: \u001b[33mtrue\u001b[39m,\n",
       "    lc_kwargs: {\n",
       "      content: \u001b[32m'You introduced yourself as \"Nemo.\"'\u001b[39m,\n",
       "      tool_calls: [],\n",
       "      invalid_tool_calls: [],\n",
       "      additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "      response_metadata: {}\n",
       "    },\n",
       "    lc_namespace: [ \u001b[32m\"langchain_core\"\u001b[39m, \u001b[32m\"messages\"\u001b[39m ],\n",
       "    content: \u001b[32m'You introduced yourself as \"Nemo.\"'\u001b[39m,\n",
       "    name: \u001b[90mundefined\u001b[39m,\n",
       "    additional_kwargs: { function_call: \u001b[90mundefined\u001b[39m, tool_calls: \u001b[90mundefined\u001b[39m },\n",
       "    response_metadata: {\n",
       "      tokenUsage: { completionTokens: \u001b[33m8\u001b[39m, promptTokens: \u001b[33m87\u001b[39m, totalTokens: \u001b[33m95\u001b[39m },\n",
       "      finish_reason: \u001b[32m\"stop\"\u001b[39m\n",
       "    },\n",
       "    tool_calls: [],\n",
       "    invalid_tool_calls: []\n",
       "  }\n",
       "]"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "await demoEphemeralChatMessageHistory.getMessages();"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that invoking the chain again will generate another summary generated from the initial summary plus new messages and so on. You could also design a hybrid approach where a certain number of messages are retained in chat history while others are summarized.\n",
    "\n",
    "## Next steps\n",
    "\n",
    "You've now learned how to manage memory in your chatbots\n",
    "\n",
    "Next, check out some of the other guides in this section, such as [how to add retrieval to your chatbot](/docs/how_to/chatbots_retrieval)."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Deno",
   "language": "typescript",
   "name": "deno"
  },
  "language_info": {
   "file_extension": ".ts",
   "mimetype": "text/x.typescript",
   "name": "typescript",
   "nb_converter": "script",
   "pygments_lexer": "typescript",
   "version": "5.3.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
